<?php
/**
 * @user magein
 * @date 2024/1/18 17:30
 */

namespace seecms\develop\controller;

use FilesystemIterator;
use magein\utils\Apipost;
use magein\utils\Faker;
use seecms\controller\SeeBaseController;
use seecms\develop\config\DevelopConfig;
use seecms\lib\builder\button\RedirectButton;
use seecms\lib\builder\column\BadgeColumn;
use seecms\lib\builder\Page;
use seecms\lib\response\HtmlResponse;
use seecms\lib\response\RedirectResponse;
use seecms\See;
use seecms\SeeLang;

class Develop extends SeeBaseController
{
    /**
     * @var \seecms\develop\lib\Develop|null
     */
    protected $parse = null;

    public function init()
    {
        $this->parse = new \seecms\develop\lib\Develop();
    }

    public function model()
    {

        if (See::request()->isAjax()) {

            $table_name = See::request()->post('table');
            $action = See::request()->post('action');

            $data = parse_db_table($table_name);

            // 字段列表
            if ($action === 'columns') {
                return "//字段列表\n [ \n" . $this->parse->columnFormatter($data['columns'], '    ') . ']';
            }

            // 字段描述
            if ($action === 'comment') {
                $data = $this->parse->columnComment($data['dictionary'], $table_name);
                return $data['zh_cn'] . "\n" . $data['en_us'];
            }

            // 模型属性
            if ($action === 'attrs') {
                $attrs = See::db()->query("show full columns from $table_name");
                return $this->parse->columnToAttrs($attrs);
            }

            // 构建页面
            if ($action === 'page') {
                return $this->parse->pageBuilder($data['columns'], $table_name);
            }
        }

        $tables = See::db()->getTables();

        $action = [
            'columns' => '字段',
            'comment' => '字典',
            'attrs' => '属性',
            'page' => '构建管理页面',
        ];

        return $this->view('develop/model', compact('tables', 'action'));
    }

    public function mock()
    {
        if (See::request()->isAjax()) {

            $table_name = See::request()->post('table');
            $limit = (int)See::request()->post('limit');
            if ($limit <= 0) {
                $limit = 10;
            }

            $attrs = See::db()->query("show full columns from $table_name");

            for ($i = 0; $i < $limit; $i++) {
                $data = Faker::mock($attrs);
                See::db()->table($table_name)->insert($data);
            }

            return $this->saveSuccess();
        }

        $tables = See::db()->getTables();

        return $this->view('develop/mock', compact('tables'));
    }

    public function apiPost()
    {
        if (See::request()->isAjax()) {

            $table_name = See::request()->post('table');
            $format = See::request()->post('format');

            $attrs = See::db()->query("show full columns from $table_name");

            $apipost = new Apipost($attrs);
            if ($format == 'page') {
                $data = $apipost->page();
            } elseif ($format == 'list') {
                $data = $apipost->lists();
            } else {
                $data = $apipost->object();
            }
            return new HtmlResponse($data);
        }

        $tables = See::db()->getTables();

        $format = [
            'object' => '对象',
            'list' => '列表',
            'page' => '分页'
        ];

        return $this->view('develop/apipost', compact('tables', 'format'));
    }

    public function icon()
    {

        $vendor = See::config()->vendor;

        $root = $vendor->root();
        $cms = $vendor->assetStatic();
        $lib = $vendor->assetLib();

        $files = [
            'icon' => $root . $cms . '/font/iconfont.css',
            'layui-icon' => $root . $lib . '/layui/css/layui.css',
        ];

        $icons = [];
        foreach ($files as $key => $file) {
            $file = trim($file, '\\');
            if (is_file($file)) {
                $content = file_get_contents($file);
                preg_match_all('/\.(' . $key . '-\w+):before/', $content, $matches);
                $icon_fonts = $matches[1] ?? [];
                if ($icon_fonts) {
                    if ($key == 'layui-icon') {
                        array_pop($icon_fonts);
                    }
                    $icons[$key] = array_chunk($icon_fonts, 10);
                }
            }
        }

        return $this->view('develop/icon', [
            'icons' => $icons
        ]);
    }

    public function transMenus()
    {
        $menus = See::table()->menu->select();

        foreach ($menus as $item) {
            $parent_id = $item['parent_id'];
            $title = $item['title'];
            $icon = $item['icon'];
            $url = $item['url'];
            $sort = $item['sort'];
            $open_type = $item['open_type'];
            echo sprintf("\$concat('%s','%s','%s','%s','%s','%s'),",
                $parent_id,
                $title,
                $icon,
                $url,
                $sort,
                $open_type
            );
            echo "<br/>";
        }
        die();
    }

    public function transRule()
    {
        $menus = See::table()->user_rule->select();

        foreach ($menus as $item) {
            $group = $item['group'];
            $title = $item['title'];
            $url = $item['url'];

            echo sprintf("\$concat('%s','%s','%s'),",
                $group,
                $title,
                $url
            );
            echo "<br/>";
        }
        die();
    }

    public function log()
    {

        $current_dir = See::request()->param('dir');
        $current_file = See::request()->param('file');
        $theme = See::request()->param('theme', 'light');
        $search_term = See::request()->param('search');
        $context_line = See::request()->param('context', 5) ?: 5;
        $auto_refresh = See::request()->param('auto_refresh', 5) ?: 5;

        $config = DevelopConfig::viewLog();

        $root = $config['root'] ?? '';
        $log_path = $config['path'] ?? [];

        $concat_log_path = function ($path) use ($root) {
            $root = rtrim($root, '/');
            $root = rtrim($root, '\\');
            return $root . '/' . trim($path, '/');
        };

        $directories = [];
        foreach ($log_path as $path) {
            $real_path = $concat_log_path($path);
            $iterator = new \RecursiveIteratorIterator(
                new \RecursiveDirectoryIterator($real_path, FilesystemIterator::SKIP_DOTS),
                \RecursiveIteratorIterator::SELF_FIRST
            );
            foreach ($iterator as $file) {
                if ($file->isDir()) {
                    $_path = str_replace($real_path, '', $file->getPathname());
                    $_path = ltrim($_path, '/');
                    $_path = ltrim($_path, '\\');
                    $directories[$path][] = $_path;
                }
            }
        }

        $files = [];
        if ($current_dir) {
            $format_size = function ($size) {
                $units = ['B', 'KB', 'MB', 'GB', 'TB'];
                $i = 0;
                while ($size >= 1024 && $i < count($units) - 1) {
                    $size /= 1024;
                    $i++;
                }
                return round($size, 2) . ' ' . $units[$i];
            };
            $full_path = $concat_log_path($current_dir);
            if (is_dir($full_path)) {
                $iterator = new \DirectoryIterator($full_path);
                foreach ($iterator as $file) {
                    if ($file->isFile() && !$file->isDot()) {
                        $files[] = [
                            'name' => $file->getFilename(),
                            'path' => $file->getPathname(),
                            'size' => $format_size($file->getSize()),
                            'modified' => date('Y-m-d H:i:s', $file->getMTime())
                        ];
                    }
                }

                // 按修改时间倒序排序
                usort($files, function ($a, $b) {
                    return strtotime($b['modified']) - strtotime($a['modified']);
                });
            }
        }

        $log_content = '';
        if ($current_file) {
            $is_file_valid = function ($file_path) use ($root, $current_dir) {
                $realpath = $root . $current_dir . '/' . $file_path;
                if (is_file($realpath)) {
                    return $realpath;
                }
                return '';
            };

            $get_log_content = function ($file_path, $search_term = null, $context_line = 5) use ($is_file_valid) {
                if (!$file_path = $is_file_valid($file_path)) {
                    return 'Invalid file path';
                }
                $content = file_get_contents($file_path);
                $lines = explode("\n", $content);
                // 倒序排列
                $lines = array_reverse($lines);

                if ($search_term) {
                    $matched_lines = [];
                    foreach ($lines as $i => $line) {
                        if (stripos($line, $search_term) !== false) {
                            $start = max(0, $i - $context_line);
                            $end = min(count($lines) - 1, $i + $context_line);
                            for ($j = $start; $j <= $end; $j++) {
                                $matched_lines[$j] = $lines[$j];
                            }
                        }
                    }
                    $lines = $matched_lines;
                }

                return implode("\n", $lines);
            };
            $log_content = $get_log_content($current_file, $search_term, $context_line);

            if (See::request()->isAjax()) {
                return $log_content;
            }
        }

        $directories = array_filter($directories);

        return $this->view('develop/log.viewer', compact(
            'directories',
            'files',
            'theme',
            'current_dir',
            'current_file',
            'log_content',
            'search_term',
            'context_line',
            'auto_refresh'
        ));
    }

    public function module()
    {

        if (See::request()->isAjax()) {
            $module_json = dirname(see_root()) . '/module.json';
            $content = [];
            if (is_file($module_json)) {
                $content = file_get_contents($module_json);
                $content = json_decode($content, true);
            }

            if ($content) {
                foreach ($content as &$item) {
                    if ($item['category'] == 1) {
                        $item['category'] = '功能扩展';
                    } elseif ($item['category'] == 2) {
                        $item['category'] = '企业应用';
                    } elseif ($item['category'] == 3) {
                        $item['category'] = '行业应用';
                    } else {
                        $item['category'] = '其他';
                    }
                    if ($item['price'] <= 0) {
                        $item['price'] = '免费';
                    }

                    $item['install'] = class_exists($item['module_path']) ? 1 : 0;
                }

                unset($item);
            }

            return $this->success(['data' => $content]);
        }

        $page = new Page();
        $page->setTitle('模块列表');
        $page->setDictionary(SeeLang::instance()->global('module'));
        $page->setLeftToolbars([]);
        $page->setActions([
            RedirectButton::init('文档')->setUrl(auth_route('develop/moduleHome'))->setMap('home', 'home'),
        ]);
        $page->setResourceRoute(
            [
                'index' => auth_route('develop/module')
            ]
        );
        $page->setColumns(
            [
                'name',
                'auth',
                'version',
                'category',
                'description',
                'price',
                BadgeColumn::init('install')->green(1, '已安装')->black(0, '未安装'),
            ]
        );

        return See::view()->page($page);
    }

    public function moduleHome()
    {
        $home = See::request()->param('home');

        if ($home) {
            return new RedirectResponse($home);
        }

        return 'forbid';
    }
}